package com.zhongpengcheng.blog.auth;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zhongpengcheng.blog.dao.pojo.db.UserDO;
import com.zhongpengcheng.blog.service.UserService;
import com.zhongpengcheng.blog.util.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 用户认证及授权逻辑
 * note: 1.记录变更 by zhongpengcheng
 *
 * @author zhongpengcheng
 * @date 2021/08/13 15:39
 **/
@Slf4j
public class AuthRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    /**
     * 授权，即授予用户角色和权限，和Shiro注解配合使用
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("doGetAuthorizationInfo..., data={}", JSON.toJSONString(principalCollection));
        return new SimpleAuthorizationInfo();
    }

    /**
     * 认证和鉴权，及验证用户账号密码是否正确，token是否有效
     * @param authenticationToken token对象
     * @throws AuthenticationException 校验失败时抛出的异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        AuthToken authToken = (AuthToken) authenticationToken;
        Object token = authToken.getCredentials();
        if (token == null) {
            // token为空，说明进行的是登录
            return doLogin(authToken);
        }
        // token不为空，进行token有效性验证
        return doAccess(authToken);
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof AuthToken;
    }

    /**
     * 执行登录逻辑，对用户密码和账号进行校验
     * @param authToken shiro token对象
     * @return 登录成功时会返回认证结果
     */
    private AuthenticationInfo doLogin(AuthToken authToken) {
        // token为空，说明进行的是登录
        UserDO user = userService.getOne(Wrappers.<UserDO>lambdaQuery().eq(UserDO::getUsername, authToken.getUsername()));
//        UserDO user = userService.getUserByUserName(authToken.getUsername());
        if (user == null) {
            // 目标用户不存在
            throw new UnknownAccountException("用户不存在!");
        }
        return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
    }

    /**
     * 执行token鉴权逻辑
     * @param authToken shiro token对象
     * @return 鉴权通过会返回认证结果
     */
    private AuthenticationInfo doAccess(AuthToken authToken) {
        String token = String.valueOf(authToken.getCredentials());
        if (TokenUtils.isTokenExpire(token)) {
            throw new IncorrectCredentialsException("Token过期");
        }
        // 根据token解析出user
        UserDO user = userService.getUserByToken(token);
        return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
    }
}
